大家好 我是 Yubin
當有一個 request 進來的時候,要如何去解析 (Parsing) 他帶過來的 Payload,會利用到 ContentTypeParser 這個東西。
Content-Type 是 HTTP Header 中的欄位,用來表達原始媒體類型 (media type)。
在 HTTP Header 中的表示語法如下:
Content-Type: text/html; charset=UTF-8
根據 Content-Type 的值不同,接收方可以做出相對應的行為來處理,例如根據不同的 type 選用不同的方式來解析進來的 Payload。
Media Type,也被稱為 Multipurpose Internet Mail Extensions 或 MIME Type
。
常見的 MIME Type 可以參考這篇:MDN:Common MIME types。
重要的 MIME Type:
application/octet-stream
二進位的預設類別,代表未知的二進制檔案,瀏覽器接收到通常不會執行會是會跳出詢問視窗。
如果 Content-Disposition
欄位被設定為 attachment
,瀏覽器會提是下載的對話框。
可以參考 MDN:Content-Disposition。
text/plain
文字檔案的預設類別。瀏覽器會愈設他們可以被呈現在畫面上。
要注意的是,text/plain 不代表任何一種文字檔案,如果用 text/plain
來傳 CSS 檔,瀏覽器不會把他當成 CSS 來解析。
text/css
要讓接收方的瀏覽器知道這個檔案是 CSS 檔,一定要用 text/css
這個 type,否則可能會被忽略。
text/html
表示該文件為 HTML 格式。
application/javascript
表示 JavaScript 文件。
application/json
表示 JSON 格式。
multipart/form-data
瀏覽器發送 HTML 表單時可以使用此類型。
image, audio, video, 等其他有的沒的 Media Type 可以參考 MDN:MIME。
預設情況下,Fastify 只有 application/json
和 text/plain
這兩種 Content-Type 的解析器,預設的字元編碼是 uft-8
。
收到其他 Content-Type 且沒有該 Parser 的時候,Fastify 會拋出 FST_ERR_CTP_INVALID_MEDIA_TYPE
的錯誤。
如果需要其他 ContentType Parser 的支援,可以透過 addContentTypeParser
API 來進行擴充。
如果需要也可以把預設的 JSON Parser 或 Plain Parser 移除。
跟其他 API 或 Plugin 一樣,addContentTypeParser
被封裝在定義的 scope 內。
也就是說如果在 root 新增 parser,則整個 Fastify App 都可以使用,如果只定義在某個 Plugin 中,只有該 Plugin 可以使用該 Parser。
要注意的是,GET
和 HEAD
這兩個 HTTP Method 的 Request Payload 永遠不會被解析。(因為他們不應該帶上 Payload。
要定義 ContentTypeParser 只需要透過 FastifyInstance 中的 .addContentTypeParser
:
import fastify, { FastifyInstance } from 'fastify'
const server: FastifyInstance = fastify()
server.addContentTypeParser('application/jsoff', function (request, payload, done) {
jsoffParser(payload, function (err, body) {
done(err, body)
})
})
.addContentTypeParser()
第一個參數是 ContentType 的名稱,型態可以是 string
或 RegExp
或string[]
。
第二個參數是 FastifyContentTypeParser
,也就是定義 parsing 的動作。
可以對多個 ContentType 用同一個 Parser 來處理:
server.addContentTypeParser(['text/xml', 'application/xml'], function (request, payload, done) {
xmlParser(payload, function (err, body) {
done(err, body)
})
})
或是透過正規表示式來套用到符合的 ContentType:
server.addContentTypeParser(/^image\/.*/, function (request, payload, done) {
imageParser(payload, function (err, body) {
done(err, body)
})
})
removeContentTypeParser
可以移除一個或多個 Parser。
removeAllContentTypeParsers
移除當前所有存在的 Parser。
如果有要 match 到所有 ContentType 的需求,可以使用 *
來進行匹配。
server.addContentTypeParser('*', function (request, payload, done) {
let data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})
雖然 Fastify 核心預設的 Parser 只有 application/json
和 text/plain
這兩種,要處理其他 ContentType 都要自己定義 Parser,聽起來很麻煩。
但 Fastify 官方或社群,已經有寫好很多現成可以用的 Parser Plugin 了,只需要 npm install
再透過 server.register()
註冊就可以了。